package com.xncoding.netty.socket.my.nio.demo00;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;
public class NioServer {
    public static void main(String[] args) throws IOException {
        int port = 6667;
        // 1、获取Selector选择器，Selector内部采用Reactor模式
        Selector selector = Selector.open();
        // 2、获取服务端通道ServerSocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 3.设置为非阻塞
        serverSocketChannel.configureBlocking(false);
        // 4、绑定连接端口
        serverSocketChannel.socket().bind(new InetSocketAddress(port), 1024);
        // 5、将通道serverSocketChannel注册到选择器selector上,并注册的操作为：“接收”操作
        //接收连接继续事件，表示服务器监听到了客户连接，服务器可以接收这个连接了
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        // 6、采用轮询的方式，查询获取“准备就绪”的注册过的操作
        while (true) {
            try {
                //6.1、1s唤醒一次，没有收到前是阻塞的
                selector.select(1000);
                // 7、获取当前选择器中所有注册的选择键（“已经准备就绪的操作”）
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                // 7.1、使用迭代器进行遍历
                Iterator<SelectionKey> it = selectionKeys.iterator();
                SelectionKey selectionKey = null;
                while (it.hasNext()) {
                    // 8、获取“准备就绪”的key
                    selectionKey = it.next();
                    it.remove();
                    try {
                        // 8.1、判断当前key是否有效
                        if (!selectionKey.isValid()) {
                            return;
                        }
                        // 9、判断key是具体的什么事件
                        if (selectionKey.isAcceptable()) { //测试此键的通道是否已准备好接受新的套接字连接。
                            // 10、若接受的事件是“接收就绪” 操作,就获取客户端连接
                            ServerSocketChannel serverSocketChannelKey = (ServerSocketChannel) selectionKey.channel();
                            // 10.1获得和客户端连接的通道
                            SocketChannel socketChannel = serverSocketChannelKey.accept();
                            // 11、切换为非阻塞模式
                            socketChannel.configureBlocking(false);
                            // 12、将该通道注册到selector选择器上
                            socketChannel.register(selector, SelectionKey.OP_READ);
                        }

                        if (selectionKey.isReadable()) {// 测试此键的通道是否已准备好进行读取
                            // 13、获取该选择器上的“读就绪”状态的通道
                            SocketChannel socketChannelKey = (SocketChannel) selectionKey.channel();
                            //14、读取数据,
                            //初始化一个1024字节的缓冲区
                            ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                            //将客户端数据读取到缓冲区，并返回读取到的字节数。
                            int readBytes = socketChannelKey.read(readBuffer);
                            //当可读字节>0时，处理客户端请求
                            if (readBytes > 0) {
                                //flip操作的作用是将limt值设置为position，poition设置为0，用于对后续缓冲区的操作。写入模式切换到读取模式
                                readBuffer.flip();
                                byte[] bytes = new byte[readBuffer.remaining()];
                                readBuffer.get(bytes);
                                String body = new String(bytes, "utf-8");
                                System.out.println("服务端收到消息是：" + body);
                                String currentTime = "服务端回复：".equalsIgnoreCase(body) ? new Date().toString() : "Bad Order";
                                //回复客户端请求
                                byte[] bytesBack = currentTime.getBytes();
                                ByteBuffer byteBuffer = ByteBuffer.allocate(bytesBack.length);
                                byteBuffer.put(bytesBack);
                                byteBuffer.flip();
                                socketChannelKey.write(byteBuffer);
                            } else if (readBytes < 0) {
                                selectionKey.cancel();
                                socketChannelKey.close();
                            } else {
                                //读到0字节，忽略
                            }
                        }
                    } catch (Exception e) {
                        if (selectionKey != null) {
                            selectionKey.cancel();
                            if (selectionKey.channel() != null) {
                                selectionKey.channel().close();
                            }
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}